package edu.unc.comp410.colemangeer.checkoutdemo;
import static java.lang.System.out;

import org.apache.log4j.Logger;

import edu.unc.comp410.colemangeer.data.ICustomerGateway;
import edu.unc.comp410.colemangeer.model.PersistentObject;
import edu.unc.comp410.colemangeer.model.ShoppingCart;
import edu.unc.comp410.colemangeer.model.constraints.CompoundZipCodeConstraint;
import edu.unc.comp410.colemangeer.model.constraints.IConstraint;
import edu.unc.comp410.colemangeer.model.constraints.IntegerConstraint;
import edu.unc.comp410.colemangeer.model.constraints.StateAbbreviationConstraint;
import edu.unc.comp410.colemangeer.model.constraints.StringConstraint;
import edu.unc.comp410.colemangeer.model.constraints.ZipCodeStringConstraint;

// "CustomerSkeleton" -cec

/**
 * Information on a single Customer: ID number, name, address, city, state,
 * zipcode, area code and phone number.
 * 
 * Interface = constructor, gettrs, settrs + constraints for each data field.
 * 
 */
public class Customer extends PersistentObject implements IConstraint {

	// Constants
	private static final String SEP = "|"; // Field separator in String
	// representation.

	// Must escape the separator character if it is to be used in a
	// regular expression. "|" is a special character for forming
	// regular expressions.
	private static final String REGEX_SEP = "\\" + SEP;
	private static final String CONSTRAINT_NAME = "CustomerConstraint";
	private static final String EMPTY_STRING = "novalue";
	private static final Integer EMPTY_INTEGER = -9999;

	private static final int NUM_FIELDS = 7;
	private static ICustomerGateway custGateway;
	private static Logger log = Logger.getLogger(Customer.class);

	// Customer info.

	// Data format requirements
	// 0) 1 <= id <= 999999
	// 1) stateAbbrev: exactly 2 capital letters
	// 2) zipCode: length = 5 characters each a digit.
	//    501 <= Integer.parse(zipCode) <= 99929
	// 3) 201 <= areaCode <= 989
	// Comp 410 - for simplicity, we will implement constraints
	// only for these data members. In general, we would 
	// have a constraint paired with each of the seven data
	// members in the class.

	// Business specific validation
	// 1) Referential integrity for the zipCode data member. This means that the
	// zipCode for a Customer must occur in the Zip table.
	// 2) Consistency for city data member. Assuming referential integrity
	// the city in the Zip table must be identical to the city in the
	// Customer object.
	// See diagram Customer Data Organization for the organization 

	// Required fields. Each Customer object must have legal entries
	// in each of these fields.
	private String name;
	private String streetAddress;
	private String city;
	private String stateAbbrev;
	private String zipCode;
	
	//sean
	private Integer custID;
	
	// Optional fields. Set to EMPTY_INTEGER or EMPTY_STRING
	// by default.
	private Integer areaCode;
	private String phoneNumber;

	// Constraints on data members.
	
	/*
	 *  added ccoleman, purely to stop compiler errors
	 *  the actual stub declarations for these have TODO: comments;
	 *  they wouldn't compile because of the ??? notation
	 *  
	 *  TODO: get rid of these after implementing the real ones
	 * */
	//private Constraint AREACODE_CONSTRAINT;
	//private Constraint STATEABBREV_CONSTRAINT;
	//private Constraint ZIPCODE_CONSTRAINT;
	
	// Customer ID
	public static final String CUSTID_NAME = "Customer ID";
	public static final IntegerConstraint CUSTID_CONSTRAINT = new IntegerConstraint(CUSTID_NAME, false);//,1,999999);

	// StateAbbrev
	public static final String STATEABBREV_NAME = "Stateabbrev";
	public static final StringConstraint STATEABBREV_CONSTRAINT = new StateAbbreviationConstraint(STATEABBREV_NAME,false);

	// ZipCode
	public static final String ZIPCODE_NAME = "ZipCode";
	public static final CompoundZipCodeConstraint ZIPCODE_CONSTRAINT = 
		//new CompoundZipCodeConstraint(ZIPCODE_NAME, new ZipCodeIntegerConstraint("ZipCode_integer",false),
				//new ZipCodeStringConstraint("string_constr", false));
		new CompoundZipCodeConstraint(ZIPCODE_NAME, new IntegerConstraint("ZipCode_integer",false),
				new ZipCodeStringConstraint("string_constr", false));

	// AreaCode
	public static final String AREACODE_NAME = "AreaCode";
	//public static final IntegerConstraint AREACODE_CONSTRAINT = new IntegerConstraint(AREACODE_NAME,true,201,989);
	//public static final IntegerConstraint AREACODE_CONSTRAINT = new AreaCodeConstraint(AREACODE_NAME,true);
	public static final IntegerConstraint AREACODE_CONSTRAINT = new IntegerConstraint(AREACODE_NAME,true);

	public static Integer getNumberOfDataFields() {
		return NUM_FIELDS + PersistentObject.getNumberOfDataFields();
	}

	/**
	 * Constructor
	 */
	public Customer(Integer theCustID, String theName, String theStreetAddress,
			String theCity, String theStateAbbrev, String theZipCode,
			Integer theAreaCode, String thePhoneNumber) {

		super(theCustID);

		log.debug("Customer constr: theCustID = " + theCustID);

		// Assign all data members.
		
		//Integer custID = theCustID;
		//sean
		custID = theCustID;
		
		name = theName;
		streetAddress = theStreetAddress;
		city = theCity;
		stateAbbrev = theStateAbbrev;
		zipCode = theZipCode;
		if (null == zipCode)
			zipCode = "99999";
		areaCode = theAreaCode;
		phoneNumber = thePhoneNumber;

		validate(this);
	}

	//sean2
	//a customer has a cart
	public Cart myCart;
	public void createCart(){
		log.warn("cart created");
		myCart = new Cart( getCustomerID() );
	}
	
	public Cart getCart(){
		return myCart;
	}
		
	public void beginCheckout(){
		CheckoutController cController = new CheckoutController( this );
		log.warn("begin checkout");
		cController.beginCheckout();
	}
	//end of sean2 additions
	
	@Override
	public Object clone() {
		Object result = new Customer(super.getId(), name, streetAddress, city,
				stateAbbrev, zipCode, areaCode, phoneNumber);
		return result;
	}

	/**
	 * 
	 */
	@Override
	public boolean equals(Object theObject) {
		if (theObject == this)
			return true;
		if (!(theObject instanceof Customer))
			return false;
		Customer theCustomer = (Customer) (theObject);
		if (!getName().equals(theCustomer.getName())
				|| !getCustomerID().equals(theCustomer.getCustomerID())
				|| !getCity().equals(theCustomer.getCity())
				|| !getStateAbbrev().equals(theCustomer.getStateAbbrev())
				|| !getAreaCode().equals(theCustomer.getAreaCode())
				|| !getPhoneNumber().equals(theCustomer.getPhoneNumber())
				|| !getStreetAddress().equals(theCustomer.getStreetAddress())
				|| !getZipCode().equals(theCustomer.getZipCode()))
			return false;
		return true;
	}

	public Integer getAreaCode() {
		return areaCode;
	}

	/**
	 * @return the city
	 */
	public String getCity() {
		return city;
	}

	public String getConstraintName() {
		return CONSTRAINT_NAME;
	}

	public Integer getCustomerID() {
		return getId();
	}

	public String getName() {
		return name;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	/**
	 * @return the stateAbbrev
	 */
	public String getStateAbbrev() {
		return stateAbbrev;
	}

	public String getStreetAddress() {
		return streetAddress;
	}

	public String getZipCode() {
		return zipCode;
	}

	/**
	 * TODO Comp 410 - fix this
	 */
	//@Override
	//public int hashCode() {
	//	return 1;
	//}
	
	//sean
	@Override
	public int hashCode() {
		
		final int prime = 31;
		int result = 17;
		result = prime * result + ((custID == null) ? 0 : custID.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result	+ ((streetAddress == null) ? 0 : streetAddress.hashCode());
		result = prime * result + ((city == null) ? 0 : city.hashCode());
		result = prime * result + ((stateAbbrev == null) ? 0 : stateAbbrev.hashCode());
		result = prime * result + ((zipCode == null) ? 0 : zipCode.hashCode());
		result = prime * result + ((areaCode == null) ? 0 : areaCode.hashCode());
		result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode());
		return result;
	}
	
	
	public boolean isLegal(Object theCustomer) {
		if (!(theCustomer instanceof Customer))
			throw new IllegalArgumentException();
		Customer cust = (Customer) (theCustomer);
		boolean custIdResult = CUSTID_CONSTRAINT.isLegal(cust.getId());
		if (! custIdResult)
			log.warn("failed Customer Id constraint" + cust);
		boolean stateAbbrevResult = STATEABBREV_CONSTRAINT.isLegal(cust.getStateAbbrev());
		if (! stateAbbrevResult)
			log.warn("failed StateAbbrev constraint" + cust);
		boolean zipCodeResult = ZIPCODE_CONSTRAINT.isLegal(cust.getZipCode());
		if (! zipCodeResult)
			log.warn("failed Zipcode constraint" + cust);
		boolean areaCodeResult = AREACODE_CONSTRAINT.isLegal(cust.getAreaCode());
		if (! areaCodeResult)
			log.warn("failed AreaCode constraint" + cust);
		return custIdResult && stateAbbrevResult && zipCodeResult
			&& areaCodeResult;
	}

	public void setAreaCode(Integer theAreaCode) {
		this.areaCode = theAreaCode;
	}

	/**
	 */
	public void setCity(String theCity) {
		this.city = theCity;
	}

	/**
	 * Used only for testing.
	 * 
	 * @param theCustomerGateway
	 */
	public void setCustomerGateway(ICustomerGateway theCustomerGateway) {
		custGateway = theCustomerGateway;
		final String msg = "Customer: setCustomerGateway: custGateway = "
				+ custGateway.toString();
		log.warn(msg);
	}

	public void setName(String theName) {
		this.name = theName;
	}

	public void setPhoneNumber(String thePhoneNumber) {
		this.phoneNumber = thePhoneNumber;
		// custGateway.store( this );
	}

	/**
	 * @param stateAbbrev
	 *            the stateAbbrev to set
	 */
	public void setStateAbbrev(String theStateAbbrev) {
		this.stateAbbrev = theStateAbbrev;
	}

	public void setStreetAddress(String theStreetAddress) {
		this.streetAddress = theStreetAddress;
		// custGateway.store( this );
	}

	public void setZipcode(String theZipCode) {
		this.zipCode = theZipCode;
		// custGateway.store( this );
	}

	/**
	 * Convert a String representation into a Customer
	 */
	public static Customer string2Object(String str) {
		out.println("str = " + str);
		String[] field = str.split(REGEX_SEP);
		if (PersistentObject.getNumberOfDataFields() + NUM_FIELDS != field.length)
			throw new IllegalArgumentException();
		Integer theCustId = Integer.parseInt(field[0]);
		String theName = field[1];
		String theStreetAddress = field[2];
		String theCity = field[3];
		String theStateAbbrev = field[4];
		String theZipCode = field[5];
		out.println("field[5] = " + field[5]);
		Integer theAreaCode = Integer.parseInt(field[6]);
		String thePhoneNumber = field[7];

		return new Customer(theCustId, theName, theStreetAddress, theCity,
				theStateAbbrev, theZipCode, theAreaCode, thePhoneNumber);
	}

	@Override
	public String toString() {
		String result = getCustomerID() + SEP + getName() + SEP
				+ getStreetAddress() + SEP + getCity() + SEP + getStateAbbrev()
				+ SEP + getZipCode() + SEP + getAreaCode() + SEP
				+ getPhoneNumber() + SEP;
		return result;
	}

	public void validate(Object theCustomer) {
		if (!(theCustomer instanceof Customer))
			throw new IllegalArgumentException();
		Customer cust = (Customer) (theCustomer);
		if (!cust.isLegal(cust)) {
			out.println("Invalid Customer = " + cust);
			throw new IllegalStateException();
		}
	}

}

